// Effective C++ 条款21：必须返回对象时，别妄想返回其引用
// 这个条款讨论了一个常见的C++错误：为了避免返回对象时的拷贝开销，错误地尝试返回对象的引用。

// 核心问题
// 条款21主要警告我们：绝不要返回指向局部对象的引用或指针。这包括：
// 1.局部栈对象
// 2.堆上分配的对象
// 3.局部静态对象

// 为什么会有这个错误
// 这个错误通常源于对效率的过度追求。开发者知道：
// 1.返回对象会导致拷贝构造函数的调用（可能开销较大）
// 2.返回引用可以避免这种拷贝
// 但这种优化在某些情况下会导致严重错误。

// 危险情况分析
// 1. 返回局部栈对象的引用
const Rational &operator*(const Rational &lhs, const Rational &rhs)
{
    Rational result(lhs.numerator() * rhs.numerator(),
                    lhs.denominator() * rhs.denominator());
    return result; // 灾难！返回了栈上即将被销毁的对象的引用
}
// 问题：函数返回后，result已被销毁，引用指向无效内存，导致未定义行为。

// 2. 返回堆对象的引用
const Rational &operator*(const Rational &lhs, const Rational &rhs)
{
    Rational *result = new Rational(lhs.numerator() * rhs.numerator(),
                                    lhs.denominator() * rhs.denominator());
    return *result; // 谁来delete这个对象？内存泄漏！
}
// 问题：虽然对象不会被销毁，但会造成内存泄漏，因为调用者无法知道需要负责释放这个对象。

// 3. 返回局部静态对象的引用
const Rational &operator*(const Rational &lhs, const Rational &rhs)
{
    static Rational result; // 静态对象
    result = Rational(lhs.numerator() * rhs.numerator(),
                      lhs.denominator() * rhs.denominator());
    return result;
}

// 问题：虽然对象不会被销毁，但在多线程环境下会有竞态条件，且连续调用会覆盖之前的结果。例如：
Rational a, b, c, d;
if ((a * b) == (c * d))
{ // 永远为true，因为两次调用返回同一个对象的引用！
  // ...
}

// 正确的做法
// 直接返回对象，让编译器优化：
inline Rational operator*(const Rational &lhs, const Rational &rhs)
{
    return Rational(lhs.numerator() * rhs.numerator(),
                    lhs.denominator() * rhs.denominator());
}
// 现代C++编译器会应用返回值优化(RVO)和移动语义来减少或消除拷贝开销。

// 何时可以返回引用
// 条款也提到了一些可以安全返回引用的情况：
// 1.返回数据成员的引用：类的方法可以返回其数据成员的引用，因为这些成员的生命周期与对象相同
class Person
{
private:
    std::string name;

public:
    const std::string &getName() const { return name; } // 安全
};

// 2.返回容器元素的引用：如operator[]返回容器中元素的引用
T &std::vector<T>::operator[](size_t index); // 安全，元素与容器同寿

// 3.返回静态对象的引用：当确实需要单例模式时
Logger &Logger::instance()
{
    static Logger logger;
    return logger; // 安全，静态对象的生命周期贯穿程序
}

// 总结
// 条款21的核心是：不要为了避免拷贝开销而返回不安全的引用。正确的做法是：
// 1.返回对象本身，信任编译器的优化能力
// 2.只在对象生命周期安全的情况下返回引用
// 3.在C++11及以后，考虑使用移动语义来减少拷贝开销
// 这个条款体现了C++的一个重要原则：正确性优先于效率，不要过早优化，尤其是以牺牲安全为代价的优化。